package ru.tennisworld.match.mappers;

import org.apache.commons.lang3.StringUtils;
import org.mapstruct.BeanMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Named;
import org.springframework.beans.factory.annotation.Autowired;
import ru.tennisworld.match.configs.MapStructConfig;
import ru.tennisworld.match.dto.integration.UserReferenceDto;
import ru.tennisworld.match.dto.integration.UserUpdateRatingRequestDto;
import ru.tennisworld.match.dto.match.CreateMatchRequestDto;
import ru.tennisworld.match.dto.match.MatchFullResponseDto;
import ru.tennisworld.match.dto.match.MatchResponseDto;
import ru.tennisworld.match.dto.match.UpdateMatchRequestDto;
import ru.tennisworld.match.entities.Match;
import ru.tennisworld.match.entities.references.MatchStage;
import ru.tennisworld.match.services.ReferenceService;
import ru.tennisworld.match.utils.IntegrationClient;

import java.util.List;
import java.util.stream.Collectors;

@Mapper(config = MapStructConfig.class,
        uses = {ReferenceMapper.class})
public abstract class MatchMapper {

    @Autowired
    private IntegrationClient integrationClient;

    @Autowired
    private ReferenceService referenceService;

    @Named("usernameListMapping")
    protected List<Long> getUserIdsList(List<String> usernameList) {
        List<UserReferenceDto> userReferenceList = integrationClient.getUserReferenceList(usernameList);
        return userReferenceList.stream().map(item -> Long.valueOf(item.getUserId())).collect(Collectors.toList());
    }

    @Mapping(target = "isStarted", constant = "false")
    @Mapping(target = "isFinished", constant = "false")
    @Mapping(target = "playersIds", source = "playersIds")
    @Mapping(target = "stage", source = "stageCode", qualifiedByName = "matchStageMapper")
    public abstract Match matToEntity(CreateMatchRequestDto dto);

    @Mapping(target = "result", source = "result")
    @Mapping(target = "isStarted", source = "isStarted", defaultValue = "false")
    @Mapping(target = "isFinished", source = "isFinished", defaultValue = "false")
    @Mapping(target = "playersIds", source = "playersIds")
    @Mapping(target = "winnerIds", source = "winnerIds")
    @Mapping(target = "stage", source = "stageCode", qualifiedByName = "matchStageMapper")
    public abstract Match matToEntity(UpdateMatchRequestDto dto);

    @Mapping(target = "matchId", expression = "java(String.valueOf(entity.getId()))")
    @Mapping(target = "isStarted", source = "isStarted")
    @Mapping(target = "isFinished", source = "isFinished")
    @Mapping(target = "result", source = "result")
    @Mapping(target = "playersIds", source = "playersIds")
    @Mapping(target = "winnerIds", source = "winnerIds")
    @Mapping(target = "matchStage", source = "stage", qualifiedByName = "matchStageMapper")
    public abstract MatchFullResponseDto mapToResponseDto(Match entity);

    @BeanMapping(ignoreByDefault = true)
    @Mapping(target = "id", ignore = true)
    @Mapping(target = "isStarted")
    @Mapping(target = "isFinished")
    @Mapping(target = "playersIds")
    @Mapping(target = "stage")
    @Mapping(target = "result", expression = "java(upd.getResult())")
    @Mapping(target = "winnerIds")
    public abstract void updateMatch(Match upd, @MappingTarget Match src);

    @BeanMapping(ignoreByDefault = true)
    @Mapping(target = "winnerIds", source = "winnerIds")
    @Mapping(target = "loserIds", expression = "java(getLoserIdsFromMatch(match))")
    public abstract UserUpdateRatingRequestDto mapToUserUpdateRequestDto(Match match);

    @Named("loserIdsMapper")
    protected List<String> getLoserIdsFromMatch(Match match) {
        return match.getPlayersIds().stream()
                .filter(item ->
                        !match.getWinnerIds().contains(item)).collect(Collectors.toList());
    }

    @Named("matchStageMapper")
    protected MatchStage getMatchStage(String stageCode) {
        if (StringUtils.isNotEmpty(stageCode)) {
            return referenceService.getMatchStageByCode(stageCode);
        }
        return null;
    }
}
